package com.docmgmt.mvp.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.docmgmt.mvp.dto.KbSyncMessage;
import com.docmgmt.mvp.dto.PagedResult;
import com.docmgmt.mvp.entity.FileEntity;
import com.docmgmt.mvp.entity.KbFileMappingEntity;
import com.docmgmt.mvp.exception.BusinessException;
import com.docmgmt.mvp.mapper.FileMapper;
import com.docmgmt.mvp.mapper.KbFileMappingMapper;
import com.docmgmt.mvp.ragflow.CreateKnowledgeBaseRequest;
import com.docmgmt.mvp.ragflow.KnowledgeBaseData;
import com.docmgmt.mvp.ragflow.RAGFlowClient;
import com.docmgmt.mvp.ragflow.dto.ChatAssistantDetailVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 知识库服务（v3.0增强版）
 *
 * v3.0 架构：
 * - 单一物理知识库：company_main_kb
 * - 文件→知识库 M:N 映射（通过 kb_file_mapping 表）
 * - 文件在RAGFlow中只存储一份（doc_id复用）
 * - 逻辑知识库通过文件映射视图构建
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class KnowledgeBaseService {

    private final RAGFlowClient ragFlowClient;
    private final KbFileMappingMapper kbFileMappingMapper;
    private final FileMapper fileMapper;
    private final RabbitTemplate rabbitTemplate;

    /**
     * v3.0 公司主知识库名称
     */
    private static final String COMPANY_MAIN_KB_NAME = "Company Main KB";

    /**
     * 知识库缓存（避免频繁调用 API）
     */
    private final ConcurrentHashMap<String, String> kbCache = new ConcurrentHashMap<>();

    /**
     * 获取或创建公司主知识库（v3.0单一物理知识库）
     *
     * @return 知识库 ID
     */
    public String getOrCreateCompanyMainKB() {
        // 先从缓存获取
        String cachedKbId = kbCache.get(COMPANY_MAIN_KB_NAME);
        if (cachedKbId != null) {
            log.debug("从缓存获取公司主知识库 ID: {}", cachedKbId);
            return cachedKbId;
        }

        try {
            // 查询现有知识库
            List<KnowledgeBaseData> kbList = ragFlowClient.listKnowledgeBases();

            // 查找是否已存在公司主知识库
            for (KnowledgeBaseData kb : kbList) {
                if (COMPANY_MAIN_KB_NAME.equals(kb.getName())) {
                    log.info("找到现有公司主知识库: id={}, name={}", kb.getId(), kb.getName());
                    kbCache.put(COMPANY_MAIN_KB_NAME, kb.getId());
                    return kb.getId();
                }
            }

            // 如果不存在，创建新的知识库
            log.info("公司主知识库不存在，开始创建: name={}", COMPANY_MAIN_KB_NAME);
            CreateKnowledgeBaseRequest request = CreateKnowledgeBaseRequest.builder()
                    .name(COMPANY_MAIN_KB_NAME)
                    .description("Company main knowledge base (v3.0 single physical KB architecture)")
                    .build();

            KnowledgeBaseData newKb = ragFlowClient.createKnowledgeBase(request);
            log.info("公司主知识库创建成功: id={}, name={}", newKb.getId(), newKb.getName());

            // 缓存新创建的知识库 ID
            kbCache.put(COMPANY_MAIN_KB_NAME, newKb.getId());

            return newKb.getId();

        } catch (Exception e) {
            log.error("获取或创建公司主知识库失败", e);
            throw new BusinessException("获取或创建公司主知识库失败: " + e.getMessage(), e);
        }
    }

    /**
     * 向后兼容方法：获取默认知识库
     */
    public String getOrCreateDefaultKnowledgeBase() {
        return getOrCreateCompanyMainKB();
    }

    /**
     * 添加文件到逻辑知识库（v3.0 M:N映射）
     *
     * @param kbId      逻辑知识库ID
     * @param fileId    文件ID
     * @param userId    操作用户ID
     */
    @Transactional
    public void addFileToKB(Long kbId, Long fileId, Long userId) {
        log.info("添加文件到知识库: kbId={}, fileId={}, userId={}", kbId, fileId, userId);

        // 1. 检查文件是否存在
        FileEntity file = fileMapper.selectById(fileId);
        if (file == null) {
            throw new BusinessException("文件不存在");
        }

        // 2. 检查文件是否已在该知识库中
        int exists = kbFileMappingMapper.existsInKb(kbId, fileId);
        if (exists > 0) {
            log.warn("文件已在知识库中: kbId={}, fileId={}", kbId, fileId);
            return;
        }

        // 2.5 检查是否有被软删除的记录，如果有就恢复
        KbFileMappingEntity deletedMapping = kbFileMappingMapper.selectOne(
            new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<KbFileMappingEntity>()
                .eq(KbFileMappingEntity::getKbId, kbId)
                .eq(KbFileMappingEntity::getFileId, fileId)
                .isNotNull(KbFileMappingEntity::getDeletedAt)
        );

        if (deletedMapping != null) {
            // 恢复软删除的记录（使用UpdateWrapper以支持设置null值）
            com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper<KbFileMappingEntity> updateWrapper =
                new com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper<>();
            updateWrapper.eq(KbFileMappingEntity::getId, deletedMapping.getId())
                .set(KbFileMappingEntity::getFileStatus, "active")
                .set(KbFileMappingEntity::getDeletedAt, null)
                .set(KbFileMappingEntity::getUpdatedAt, LocalDateTime.now());
            kbFileMappingMapper.update(null, updateWrapper);
            log.info("恢复软删除的映射记录: kbId={}, fileId={}, mappingId={}", kbId, fileId, deletedMapping.getId());

            // 4. 发送同步消息到RAGFlow（如果文件还未同步到RAGFlow）
            if (file.getRagflowDocId() == null || file.getRagflowDocId().isEmpty()) {
                triggerKBSync(fileId, "UPLOAD");
            }
            return;
        }

        // 3. 创建映射关系
        KbFileMappingEntity mapping = new KbFileMappingEntity();
        mapping.setKbId(kbId);
        mapping.setFileId(fileId);
        mapping.setFileStatus("active");
        mapping.setAddedBy(userId);
        mapping.setAddedAt(LocalDateTime.now());
        kbFileMappingMapper.insert(mapping);

        log.info("文件已添加到知识库: kbId={}, fileId={}, mappingId={}", kbId, fileId, mapping.getId());

        // 4. 发送同步消息到RAGFlow（如果文件还未同步到RAGFlow）
        if (file.getRagflowDocId() == null || file.getRagflowDocId().isEmpty()) {
            triggerKBSync(fileId, "UPLOAD");
        }
    }

    /**
     * 从逻辑知识库移除文件（v3.0软删除）
     *
     * @param kbId   知识库ID
     * @param fileId 文件ID
     * @param userId 操作用户ID
     */
    @Transactional
    public void removeFileFromKB(Long kbId, Long fileId, Long userId) {
        log.info("从知识库移除文件: kbId={}, fileId={}, userId={}", kbId, fileId, userId);

        // 查找映射关系
        LambdaQueryWrapper<KbFileMappingEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(KbFileMappingEntity::getKbId, kbId)
               .eq(KbFileMappingEntity::getFileId, fileId)
               .eq(KbFileMappingEntity::getFileStatus, "active");

        KbFileMappingEntity mapping = kbFileMappingMapper.selectOne(wrapper);
        if (mapping == null) {
            throw new BusinessException("文件不在该知识库中");
        }

        // 软删除：标记为 deleted 状态
        mapping.setFileStatus("deleted");
        mapping.setDeletedAt(LocalDateTime.now());
        kbFileMappingMapper.updateById(mapping);

        log.info("文件已从知识库移除: kbId={}, fileId={}", kbId, fileId);

        // 检查文件是否还在其他知识库中
        List<KbFileMappingEntity> otherMappings = kbFileMappingMapper.selectKbsByFileId(fileId);
        if (otherMappings.isEmpty()) {
            // 如果文件不再属于任何知识库，触发从RAGFlow删除
            log.info("文件不再属于任何知识库，触发从RAGFlow删除: fileId={}", fileId);
            triggerKBSync(fileId, "DELETE");
        }
    }

    /**
     * 获取知识库中的文件列表（分页）
     *
     * @param kbId     知识库ID
     * @param page     页码
     * @param pageSize 每页大小
     * @return 分页结果
     */
    public PagedResult<FileEntity> getKBFiles(Long kbId, Integer page, Integer pageSize) {
        log.info("获取知识库文件列表: kbId={}, page={}, pageSize={}", kbId, page, pageSize);

        // 默认值
        int currentPage = page != null && page > 0 ? page : 1;
        int size = pageSize != null && pageSize > 0 ? pageSize : 20;

        // 1. 查询映射关系
        List<KbFileMappingEntity> mappings = kbFileMappingMapper.selectActiveFilesByKbId(kbId);
        List<Long> fileIds = mappings.stream()
                .map(KbFileMappingEntity::getFileId)
                .collect(Collectors.toList());

        if (fileIds.isEmpty()) {
            return PagedResult.<FileEntity>builder()
                    .items(List.of())
                    .pagination(PagedResult.Pagination.builder()
                            .page(currentPage)
                            .pageSize(size)
                            .total(0L)
                            .totalPages(0)
                            .build())
                    .build();
        }

        // 2. 分页查询文件
        LambdaQueryWrapper<FileEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(FileEntity::getId, fileIds)
               .orderByDesc(FileEntity::getCreatedAt);

        Page<FileEntity> pageResult = new Page<>(currentPage, size);
        fileMapper.selectPage(pageResult, wrapper);

        // 3. 构建结果
        long total = pageResult.getTotal();
        int totalPages = (int) Math.ceil((double) total / size);

        PagedResult<FileEntity> result = PagedResult.<FileEntity>builder()
                .items(pageResult.getRecords())
                .pagination(PagedResult.Pagination.builder()
                        .page(currentPage)
                        .pageSize(size)
                        .total(total)
                        .totalPages(totalPages)
                        .build())
                .build();

        log.info("获取知识库文件列表成功: kbId={}, total={}", kbId, total);
        return result;
    }

    /**
     * 获取文件所属的知识库列表
     *
     * @param fileId 文件ID
     * @return 知识库ID列表
     */
    public List<Long> getFileKBs(Long fileId) {
        log.info("获取文件所属知识库: fileId={}", fileId);

        List<KbFileMappingEntity> mappings = kbFileMappingMapper.selectKbsByFileId(fileId);
        List<Long> kbIds = mappings.stream()
                .map(KbFileMappingEntity::getKbId)
                .collect(Collectors.toList());

        log.info("文件属于 {} 个知识库", kbIds.size());
        return kbIds;
    }

    /**
     * 触发知识库同步（发送消息到RabbitMQ）
     *
     * @param fileId 文件ID
     * @param action 操作类型（UPLOAD/UPDATE/DELETE）
     */
    public void triggerKBSync(Long fileId, String action) {
        log.info("触发知识库同步: fileId={}, action={}", fileId, action);

        // 构造同步消息
        KbSyncMessage message = new KbSyncMessage();
        message.setFileId(fileId);
        message.setAction(action);
        message.setRetryCount(0);

        // 发送到RabbitMQ队列
        rabbitTemplate.convertAndSend("ragflow.sync.queue", message);

        log.info("同步消息已发送: fileId={}, action={}", fileId, action);
    }

    /**
     * 批量添加文件到知识库
     *
     * @param kbId    知识库ID
     * @param fileIds 文件ID列表
     * @param userId  操作用户ID
     */
    @Transactional
    public void batchAddFilesToKB(Long kbId, List<Long> fileIds, Long userId) {
        log.info("批量添加文件到知识库: kbId={}, fileCount={}", kbId, fileIds.size());

        for (Long fileId : fileIds) {
            try {
                addFileToKB(kbId, fileId, userId);
            } catch (Exception e) {
                log.error("添加文件失败: fileId={}, error={}", fileId, e.getMessage());
                // 继续处理下一个文件
            }
        }

        log.info("批量添加文件完成: kbId={}, total={}", kbId, fileIds.size());
    }

    /**
     * 批量从知识库移除文件
     *
     * @param kbId    知识库ID
     * @param fileIds 文件ID列表
     * @param userId  操作用户ID
     */
    @Transactional
    public void batchRemoveFilesFromKB(Long kbId, List<Long> fileIds, Long userId) {
        log.info("批量从知识库移除文件: kbId={}, fileCount={}", kbId, fileIds.size());

        for (Long fileId : fileIds) {
            try {
                removeFileFromKB(kbId, fileId, userId);
            } catch (Exception e) {
                log.error("移除文件失败: fileId={}, error={}", fileId, e.getMessage());
                // 继续处理下一个文件
            }
        }

        log.info("批量移除文件完成: kbId={}, total={}", kbId, fileIds.size());
    }

    /**
     * 清除知识库缓存（用于测试或故障恢复）
     */
    public void clearCache() {
        kbCache.clear();
        log.info("知识库缓存已清除");
    }

    // ==================== RAGFlow v0.21.1 Chat Assistant 管理 ====================

    /**
     * Chat Assistant缓存（避免频繁创建）
     */
    private final ConcurrentHashMap<String, String> chatAssistantCache = new ConcurrentHashMap<>();

    /**
     * 获取或创建公司主知识库的Chat Assistant
     *
     * RAGFlow v0.21.1要求：必须先创建Chat Assistant才能进行对话
     *
     * @return Chat Assistant ID
     */
    public String getOrCreateMainChatAssistant() {
        String cacheKey = "company_main_chat_assistant";

        // 先从缓存获取
        String cachedChatId = chatAssistantCache.get(cacheKey);
        if (cachedChatId != null) {
            log.debug("从缓存获取Chat Assistant ID: {}", cachedChatId);
            return cachedChatId;
        }

        try {
            // 获取公司主知识库ID
            String kbId = getOrCreateCompanyMainKB();

            // v3.1修复：先查询是否已有同名的Chat Assistant，如果有则复用
            // 原因：RAGFlow的Session绑定到Chat Assistant，不能跨Chat Assistant使用
            String fixedName = "Company Main Chat Assistant";

            // 查询所有Chat Assistant
            List<ChatAssistantDetailVO> existingChats = ragFlowClient.listChatAssistants(1, 100);

            // 查找同名的Chat Assistant
            String chatId = null;
            for (ChatAssistantDetailVO chat : existingChats) {
                if (fixedName.equals(chat.getName())) {
                    chatId = chat.getId();
                    log.info("找到现有的Chat Assistant: id={}, name={}", chatId, fixedName);
                    break;
                }
            }

            // 如果没有找到，则创建新的
            if (chatId == null) {
                log.info("创建Chat Assistant（绑定知识库，使用doc_ids动态过滤文档）: kbId={}", kbId);
                chatId = ragFlowClient.createChatAssistant(
                    fixedName,
                    List.of(kbId)  // 绑定知识库：定义检索范围，doc_ids在此范围内过滤
                );
            }

            // 缓存
            chatAssistantCache.put(cacheKey, chatId);
            log.info("Chat Assistant创建成功: chatId={}", chatId);

            return chatId;

        } catch (Exception e) {
            log.error("获取或创建Chat Assistant失败", e);
            throw new BusinessException("获取或创建Chat Assistant失败: " + e.getMessage(), e);
        }
    }

    /**
     * 清除Chat Assistant缓存
     */
    public void clearChatAssistantCache() {
        chatAssistantCache.clear();
        log.info("Chat Assistant缓存已清除");
    }
}
